﻿using System;
using System.Collections.Generic;
using Messenia.ExceptionHandler;
using NHibernate;
using NHibernate.Criterion;

namespace Messenia.Data.Hibernate {
    public abstract class DataManager<TX> : IDisposable {
        //private static ISessionFactory sessionFactory;
        /// <summary>
        /// Configures Log4Net to work with NHibernate.
        /// </summary>
        private void ConfigureLog4Net() {
            log4net.Config.XmlConfigurator.Configure();
        }
        /// <summary>
        /// Saves an object and its persistent children.
        /// </summary>
        public void Save(object item) {
            using (var session = StaticHibernate.Instance.SessionFactory.OpenSession()) {
                using (var transaction = session.BeginTransaction()) {
                    try {
                        session.SaveOrUpdate(item);
                        transaction.Commit();
                    } catch (Exception ex) {
                        transaction.Rollback();
                        throw new Exception("Could not save curent entity " + ex.Message);
                    } finally {
                        session.Close();
                    }

                }
            }
        }
        /// <summary>
        /// Saves an object .
        /// </summary>
        public void Update(object item) {
            using (var session = StaticHibernate.Instance.SessionFactory.OpenSession()) {
                using (var transaction = session.BeginTransaction()) {
                    try {
                        session.Update(item);
                        transaction.Commit();
                    } catch (Exception ex) {
                        transaction.Rollback();
                        throw new Exception("Could not save curent entity " + ex.Message);
                    } finally {
                        session.Close();

                    }

                }
            }
        }
        /// <summary>
        /// Delete an object of a specified type.
        /// </summary>
        /// <param name="item">The item to delete.</param>
        /// <typeparam name="T">The type of objects to delete.</typeparam>
        public void Delete(object item) {
            using (ISession session = StaticHibernate.Instance.SessionFactory.OpenSession()) {
                using (session.BeginTransaction()) {
                    session.Delete(item);
                    session.Transaction.Commit();
                }
            }
        }

        /// <summary>
        /// Deletes objects of a specified type.
        /// </summary>
        /// <param name="itemsToDelete">The items to delete.</param>
        /// <typeparam name="T">The type of objects to delete.</typeparam>
        public void Delete(IList<object> itemsToDelete) {
            using (var session = StaticHibernate.Instance.SessionFactory.OpenSession()) {
                foreach (var item in itemsToDelete) {
                    using (session.BeginTransaction()) {
                        session.Delete(item);
                        session.Transaction.Commit();
                    }
                }
            }
        }
        /// <summary>
        /// Get object by Proxy class criteria
        /// </summary>
        /// <typeparam name="T">the objects type</typeparam>
        /// <param name="propertyName">The name of Proxy Class property </param>
        /// <param name="proxyClass">The name of property class</param>
        /// <param name="value"> the value to compare</param>
        /// <param name="sortProperty">property to sort </param>
        /// <param name="ascending">sort order </param>
        /// <param name="idUser">user that call this method</param>
        /// <returns>A list of objects tha corespond to curent request</returns>
        public IList<T> GetAllObjectsByProxy<T>(string propertyName , string proxyClass , object value , string sortProperty , bool ascending , int idUser) {
            if (!ascending) {
                return GetAllObjectsByProxy<T>(propertyName , proxyClass , value , idUser);
            }

            using (var session = StaticHibernate.Instance.SessionFactory.OpenSession()) {
                try {
                    return session.CreateCriteria(typeof(T))
                                               .AddOrder(Order.Asc(sortProperty))
                                               .CreateCriteria(proxyClass)
                                               .Add(Expression.Eq(propertyName , value))
                                               .List<T>();

                } catch (HibernateException exc) {
                    ExceptionManager.Handling.ProcessException(
                        new ApplicationException(exc.Message , exc) , idUser);
                }
            }

            return new List<T>();
        }

        /// <summary>
        /// A list of objects that correspond to criteria 
        /// </summary>
        /// <typeparam name="T">the returnin objects type</typeparam>
        /// <param name="propertyName">The name of property of proxy class</param>
        /// <param name="proxyClass">The name of proxy class</param>
        /// <param name="value">Value to compare</param>
        /// <param name="idUser">USer that coll this method</param>
        /// <returns>A list of objects of type T</returns>
        public IList<T> GetAllObjectsByProxy<T>(string propertyName , string proxyClass , object value , int idUser) {
            using (var session = StaticHibernate.Instance.SessionFactory.OpenSession()) {

                try {
                    return session.CreateCriteria(typeof(T))
                         .CreateCriteria(proxyClass)
                         .Add(Expression.Eq(propertyName , value)).List<T>();

                } catch (HibernateException exc) {
                    ExceptionManager.Handling.ProcessException(new ApplicationException(exc.Message , exc) , idUser);
                }
            }
            return (IList<T>)new object();
        }
        /// <summary>
        /// return a colection of objects that corespond to concrete criteria 
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="propertyName"></param>
        /// <param name="value"></param>
        /// <param name="idUser"></param>
        /// <returns></returns>
        public IList<T> GetAllObjectsByProperty<T>(string propertyName , object value , int idUser) {
            using (var session = StaticHibernate.Instance.SessionFactory.OpenSession()) {
                try {
                    return session.CreateCriteria(typeof(T)).Add(Expression.Eq(propertyName , value)).List<T>();

                } catch (HibernateException exc) {
                    ExceptionManager.Handling.ProcessException(new ApplicationException(exc.Message , exc) , idUser);
                }
            }
            return (IList<T>)new object();
        }

        public IList<T> GetAllObjectsByProperty<T>(string propertyName , object value , string sortProperty , bool ascending , int idUser) {
            IList<T> listOfObjects = new List<T>();
            using (var session = StaticHibernate.Instance.SessionFactory.OpenSession()) {

                var crit = session.CreateCriteria(typeof(T)).Add(Expression.Eq(propertyName , value));
                try {
                    Order ord;

                    if (ascending) {
                        ord = Order.Asc(sortProperty);
                    } else {
                        ord = Order.Desc(sortProperty);
                    }
                    listOfObjects = crit.AddOrder(ord).List<T>();
                } catch (HibernateException exc) {
                    ExceptionManager.Handling.ProcessException(new ApplicationException(exc.Message , exc) , idUser);
                }
            }
            return listOfObjects;
        }

        public IList<TX> GetAll(string sortProperty , bool ascending , int idUser) {
            using (var session = StaticHibernate.Instance.SessionFactory.OpenSession()) {
                try {
                    var crit = session.CreateCriteria(typeof(TX));
                    IList<TX> listOfObjects ;
                    if (ascending) {
                        crit.AddOrder(Order.Asc(sortProperty));
                        listOfObjects = crit.List<TX>();
                    } else {
                        crit.AddOrder(Order.Desc(sortProperty));
                        listOfObjects = crit.List<TX>();
                    }
                    return listOfObjects;
                } catch (HibernateException exc) {
                    ExceptionManager.Handling.ProcessException(new ApplicationException(exc.Message , exc) , idUser);
                }
            }
            return (IList<TX>)new object();
        }
        public IList<TX> GetAll(int idUser) {
            using (var session = StaticHibernate.Instance.SessionFactory.OpenSession()) {
                try {
                    var crit = session.CreateCriteria(typeof(TX));
                    return crit.List<TX>();
                } catch (HibernateException exc) {
                    ExceptionManager.Handling.ProcessException(new ApplicationException(exc.Message , exc) , idUser);
                }
            }
            return (IList<TX>)new object();
        }


        /// <summary>
        /// Close this Persistence Manager and release all resources (connection pools, etc). It is the responsibility of the application to ensure that there are no open Sessions before calling Close().
        /// </summary>
        public void Close() {
            StaticHibernate.Instance.SessionFactory.Close();
        }

        #region Constructor

        /// <summary>
        /// Default constructor.
        /// </summary>
        public DataManager() {
            ConfigureLog4Net();         

        }

        #endregion

        #region IDisposable Members

        public void Dispose() {
            StaticHibernate.Instance.SessionFactory.Dispose();
        }

        #endregion
    }
}
